﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Algobox.Objects.Finance.Assets.Stock;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Diagnostics;

namespace Algobox.Data.Finance.LiquidTickData
{
    public class LiquidTickDataInterface : IDisposable
    {
        public LiquidTickDataInterface()
        {
            _linq = new LiquidTickDataTEST01DataContext();
            _continueBulkInsert = true;
            _flushCounter = 0;
            _counterLasts = 0;

            _bulkCopy = new SqlBulkCopy(_linq.Connection.ConnectionString);
            InitialiseBulkCopy();
        }

        public void Dispose()
        {
            _linq.Dispose();
            _continueBulkInsert = false;
        }

        const string TABLELASTS = "LiquidView.dbo.PricesIntradayTrades";
        const int MAX_LASTS_FLUSH = 50;

        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
        
        LiquidTickDataTEST01DataContext _linq;
        DataTable _dataTableLast;
        SqlBulkCopy _bulkCopy;
        Thread _rowDataQueueThread;
        bool _continueBulkInsert;
        long _counterLasts;
        int _flushCounter;

        public event Action<int, TimeSpan> OnDatabaseWriteLasts;

        public string FeedName { get { return "LiquidTickDataInterface"; } }

        /// <summary>
        /// Initialises the DataTables for use with the bulk copy
        /// Use SqlBulkCopyColumnMapping for mapping columns in DataTable to table def in database
        /// http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopycolumnmapping.aspx
        /// </summary>
        private void InitialiseBulkCopy()
        {
            if (_dataTableLast == null)
            {
                _dataTableLast = new DataTable(TABLELASTS);
                DataColumn dc;
                dc = new DataColumn("UpdateDate", typeof(DateTime));
                dc.AllowDBNull = false;
                _dataTableLast.Columns.Add(dc);
                dc = new DataColumn("UpdateTime", typeof(TimeSpan));
                dc.AllowDBNull = false;
                _dataTableLast.Columns.Add(dc);
                dc = new DataColumn("Symbol", typeof(string));
                dc.AllowDBNull = false;
                _dataTableLast.Columns.Add(dc);
                dc = new DataColumn("LastPrice", typeof(decimal));
                dc.AllowDBNull = false;
                _dataTableLast.Columns.Add(dc);
                dc = new DataColumn("LastVolume", typeof(long));
                dc.AllowDBNull = false;
                _dataTableLast.Columns.Add(dc);
                dc = new DataColumn("Phase", typeof(string));
                dc.AllowDBNull = false;
                _dataTableLast.Columns.Add(dc);
            }
            if (_rowDataQueueThread == null)
            {
                _rowDataQueueThread = new Thread(new ThreadStart(this.ProcessBulkCopyQueue));
                _rowDataQueueThread.Name = FeedName;
                _rowDataQueueThread.Priority = ThreadPriority.BelowNormal;
                _rowDataQueueThread.Start();
            }
        }

        public bool QueueBulkInsertLastPrice(IStock stock, Objects.Finance.Prices.PriceVolume last)
        {
            DataRow rowLast = _dataTableLast.NewRow();
            rowLast["UpdateDate"] = last.TimeStamp.Date;
            rowLast["UpdateTime"] = last.TimeStamp.TimeOfDay;
            rowLast["Symbol"] = stock.Symbol;
            rowLast["LastPrice"] = (decimal)last.Price;
            rowLast["LastVolume"] = last.Volume;
            rowLast["Phase"] = stock.Phase.ToString();

            try
            {
                if (_continueBulkInsert)
                {
                    lock (_dataTableLast)
                    {
                        _dataTableLast.Rows.Add(rowLast);
                    }
                }
            }
            catch (Exception e)
            {
                Log.Debug("QueueBulkInsertLastPrice Lost data point due to " + e.ToString());
            }

            return true;
        }


        public void QueueQuoteUpdate(DateTime timeStamp, string symbol, string phase, double lastPrice, int lastSize, bool lastUpdated, double bidPrice, int bidSize, bool bidUpdated, double askPrice, int askSize, bool askUpdated)
        {
            
        }

        private void ProcessBulkCopyQueue()
        {
            DataTable cloneTable;
            Stopwatch stopWatchBBO = Stopwatch.StartNew();
            Stopwatch stopwatchLast = Stopwatch.StartNew();

            // second delay before saving data when there is no updates
            int counterMAX = 120;
            int countBBO = 0;
            int counterBBO = 0;
            int countLast = 0;
            int counterLast = 0;
            long elapsed = 1;

            while (_continueBulkInsert)
            {
                if (_dataTableLast.Rows.Count > MAX_LASTS_FLUSH
                    || counterLast >= counterMAX
                    || _flushCounter > 0)
                {
                    stopWatchBBO.Stop();
                    stopwatchLast.Stop();
                    elapsed = stopwatchLast.ElapsedMilliseconds == 0 ? 1 : stopwatchLast.ElapsedMilliseconds;
                    stopwatchLast.Reset();
                    Log.Info("{0} Lasts collected in {1:0.00} seconds",
                        _dataTableLast.Rows.Count,
                        elapsed / 1000.0);
                    /*
                    if (OnDatabaseWriteLasts != null)
                    {
                        OnDatabaseWriteLasts
                    }
                    */
                    lock (_dataTableLast)
                    {
                        cloneTable = _dataTableLast.Copy();
                        _dataTableLast.Rows.Clear();
                    }
                    _bulkCopy.DestinationTableName = TABLELASTS;
                    try
                    {
                        stopwatchLast.Start();
                        // bulk copy the data to the database
#if !DEBUG
                        _bulkCopy.WriteToServer(cloneTable);
#endif
                        Log.Info("{0} Lasts saved to database in {1} milliseconds",
                            cloneTable.Rows.Count,
                            stopwatchLast.ElapsedMilliseconds);
                        stopwatchLast.Reset();
                        _counterLasts += cloneTable.Rows.Count;
                    }
                    catch (Exception e)
                    {
                        Log.Error("Failed to bulk write Lasts to server, error message was: " + e.ToString());
                        lock (_dataTableLast)
                        {
                            _dataTableLast.Merge(cloneTable);
                        }
                    }
                    cloneTable.Clear();
                    stopwatchLast.Start();
                    stopWatchBBO.Start();
                    countLast = 0;
                    counterLast = 0;
                }
                else if (_dataTableLast.Rows.Count > 0)
                {
                    if (_dataTableLast.Rows.Count == countLast)
                    {
                        ++counterLast;
                    }
                    else
                    {
                        counterLast = 0;
                        countLast = _dataTableLast.Rows.Count;
                    }
                }

                if (_flushCounter > 0)
                    --_flushCounter;

                if (_flushCounter == 0)
                    Thread.Sleep(1000);
            }
        }

        public bool TryGetStocks(out List<IStock> stocks)
        {
            stocks = new List<IStock>();
            try
            {
                foreach (string symbol in _linq.PricesIntradayTrades.Select(a => a.Symbol).Distinct())
                {
                    stocks.Add(new StockSecurity(symbol, Objects.Finance.Markets.MarketMIC.XXXX));
                }
                return true;
            }
            catch { }
            return false;
        }

    }
}
